From: Tim Deegan Date: Thu, 6 Jan 2011 16:58:48 +0000 (+0000) Subject: mem_sharing: fix race condition of nominate and unshare X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~10988^2 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22Dat/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22Dat?a=commitdiff_plain;h=6b43e7533c3cb120765bf0693a3f3fc13ed168b2;p=xen.git mem_sharing: fix race condition of nominate and unshare (1) When updating/checking p2m type for mem_sharing, we must hold shr_lock (2) For nominate operation, if the page is already nominated, return the handle from page_info->shr_handle (3) For unshare operation, it is possible that multiple users unshare a page via hvm_hap_nested_page_fault() at the same time. If the page is already un-shared by someone else, simply return success. Signed-off-by: Jui-Hao Chiang Signed-off-by: Han-Lin Li Acked-by: Tim Deegan --- diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c index 0fe17235ee..e3d0f6cdc7 100644 --- a/xen/arch/x86/mm/mem_sharing.c +++ b/xen/arch/x86/mm/mem_sharing.c @@ -502,6 +502,7 @@ int mem_sharing_nominate_page(struct p2m_domain *p2m, *phandle = 0UL; + shr_lock(); mfn = gfn_to_mfn(p2m, gfn, &p2mt); /* Check if mfn is valid */ @@ -509,29 +510,33 @@ int mem_sharing_nominate_page(struct p2m_domain *p2m, if (!mfn_valid(mfn)) goto out; + /* Return the handle if the page is already shared */ + page = mfn_to_page(mfn); + if (p2m_is_shared(p2mt)) { + *phandle = page->shr_handle; + ret = 0; + goto out; + } + /* Check p2m type */ if (!p2m_is_sharable(p2mt)) goto out; /* Try to convert the mfn to the sharable type */ - page = mfn_to_page(mfn); ret = page_make_sharable(d, page, expected_refcnt); if(ret) goto out; /* Create the handle */ ret = -ENOMEM; - shr_lock(); handle = next_handle++; if((hash_entry = mem_sharing_hash_insert(handle, mfn)) == NULL) { - shr_unlock(); goto out; } if((gfn_info = mem_sharing_gfn_alloc()) == NULL) { mem_sharing_hash_destroy(hash_entry); - shr_unlock(); goto out; } @@ -545,7 +550,6 @@ int mem_sharing_nominate_page(struct p2m_domain *p2m, BUG_ON(page_make_private(d, page) != 0); mem_sharing_hash_destroy(hash_entry); mem_sharing_gfn_destroy(gfn_info, 0); - shr_unlock(); goto out; } @@ -559,11 +563,11 @@ int mem_sharing_nominate_page(struct p2m_domain *p2m, gfn_info->domain = d->domain_id; page->shr_handle = handle; *phandle = handle; - shr_unlock(); ret = 0; out: + shr_unlock(); return ret; } @@ -633,14 +637,21 @@ int mem_sharing_unshare_page(struct p2m_domain *p2m, struct list_head *le; struct domain *d = p2m->domain; + mem_sharing_audit(); + /* Remove the gfn_info from the list */ + shr_lock(); + mfn = gfn_to_mfn(p2m, gfn, &p2mt); + + /* Has someone already unshared it? */ + if (!p2m_is_shared(p2mt)) { + shr_unlock(); + return 0; + } page = mfn_to_page(mfn); handle = page->shr_handle; - mem_sharing_audit(); - /* Remove the gfn_info from the list */ - shr_lock(); hash_entry = mem_sharing_hash_lookup(handle); list_for_each(le, &hash_entry->gfns) { @@ -707,7 +718,6 @@ private_page_found: mem_sharing_hash_delete(handle); else atomic_dec(&nr_saved_mfns); - shr_unlock(); if(p2m_change_type(p2m, gfn, p2m_ram_shared, p2m_ram_rw) != p2m_ram_shared) @@ -718,6 +728,7 @@ private_page_found: /* Update m2p entry */ set_gpfn_from_mfn(mfn_x(page_to_mfn(page)), gfn); + shr_unlock(); return 0; }